<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      //Object.defineProperty()方法会直接在一个对象上定义一个新属性，或者修改一个对象的现有属性，并返回此对象

      // const { log } = require('console')

      //备注：此方法应当直接在Object构造器对象上调用此方法，而不是任意一个Object类型的实例上调用
      // const object1 = {}
      // Object.defineProperty(object1, 'property1', {
      //   value: 42,
      //   writable: false,
      // })
      // object1.property1 = 77
      // console.log(object1.property1)

      //参数：obj:要定义属性的对象，prop:要定义或修改的属性的名称或symbol，descriptor:要定义或修改的属性描述符
      //返回值：被传递给函数的对象
      //使用_proto_
      // let obj = {}
      // let descriptor = Object.create(null) //没有继承的属性
      // //默认没有enumerable，没有configurable,没有writable，
      // descriptor.value = 'static'
      // Object.defineProperty(obj, 'key', descriptor)
      // //显示
      // Object.defineProperty(obj, 'key', {
      //   enumerable: false,
      //   configurable: false,
      //   writable: false,
      //   value: 'static',
      // })

      //hasOwnProperty：方法返回一个布尔值，指示对象自身属性中是否具有指定的属性，(也就是，是否有指定的键)
      //property1：就是属性
      // const obj = {}
      // obj.property1 = 42
      // console.log(obj.hasOwnProperty('property1'))
      // console.log(obj.hasOwnProperty('toString'))

      //propertyIsEnumerable() 方法返回一个布尔值，表示指定的属性是否可枚举。判断对象指定的属性是否可以fon in循环

      // const obj2 = {}
      // const array = []
      // obj2.property1 = 42
      // array[0] = 42

      // console.log(array.propertyIsEnumerable(0))

      //entries() 方法返回一个新的Array Iterator对象，该对象包含数组中每个索引的键/值对。
      const array = ['a', 'b', 'c', 'd']
      const iter = array.entries()
      console.log(iter.next().value)
      console.log(iter.next().value)
      //iterator.next()返回一个对象，对于有元素的数组，
      // 是next{ value: Array(2), done: false }；
      // next.done 用于指示迭代器是否完成：在每次迭代时进行更新而且都是false，
      // 直到迭代器结束done才是true。
      // next.value是一个["key","value"]的数组，是返回的迭代器中的元素值。

      //Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组，数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
      // simple array
      // var arr = ['a', 'b', 'c']
      // console.log(Object.keys(arr)) // console: ['0', '1', '2']

      // // array like object
      // var obj = { 0: 'a', 1: 'b', 2: 'c' }
      // console.log(Object.keys(obj)) // console: ['0', '1', '2']

      // // array like object with random key ordering
      // var anObj = { 100: 'a', 2: 'b', 7: 'c' }
      // console.log(Object.keys(anObj)) // console: ['2', '7', '100']

      // // getFoo is a property which isn't enumerable
      // var myObj = Object.create(
      //   {},
      //   {
      //     getFoo: {
      //       value: function () {
      //         return this.foo
      //       },
      //     },
      //   }
      // )
      // myObj.foo = 1
      // console.log(Object.keys(myObj)) // console: ['foo']

      //Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组，值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
      // var obj = { foo: 'bar', baz: 42 }
      // console.log(Object.values(obj)) // ['bar', 42]

      // // array like object
      // var obj = { 0: 'a', 1: 'b', 2: 'c' }
      // console.log(Object.values(obj)) // ['a', 'b', 'c']

      // // array like object with random key ordering
      // // when we use numeric keys, the value returned in a numerical order according to the keys
      // var an_obj = { 100: 'a', 2: 'b', 7: 'c' }
      // console.log(Object.values(an_obj)) // ['b', 'c', 'a']

      // // getFoo is property which isn't enumerable
      // var my_obj = Object.create(
      //   {},
      //   {
      //     getFoo: {
      //       value: function () {
      //         return this.foo
      //       },
      //     },
      //   }
      // )
      // my_obj.foo = 'bar'
      // console.log(Object.values(my_obj)) // ['bar']

      // // non-object argument will be coerced to an object
      // console.log(Object.values('foo')) // ['f', 'o', 'o']

      //Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改；冻结了一个对象则不能向这个对象添加新的属性，不能删除已有属性，不能修改该对象已有属性的可枚举性、可配置性、可写性，以及不能修改已有属性的值。此外，冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

      const obj = {
        prop: 42,
      }

      Object.freeze(obj)

      obj.prop = 33
      // Throws an error in strict mode

      console.log(obj.prop)
      // expected output: 42

      //Object.isFrozen()方法判断一个对象是否被冻结。
      // 一个对象默认是可扩展的,所以它也是非冻结的.
      Object.isFrozen({}) // === false

      // 一个不可扩展的空对象同时也是一个冻结对象.
      var vacuouslyFrozen = Object.preventExtensions({})
      Object.isFrozen(vacuouslyFrozen) //=== true;

      // 一个非空对象默认也是非冻结的.
      var oneProp = { p: 42 }
      Object.isFrozen(oneProp) //=== false

      // 让这个对象变的不可扩展,并不意味着这个对象变成了冻结对象,
      // 因为p属性仍然是可以配置的(而且可写的).
      Object.preventExtensions(oneProp)
      Object.isFrozen(oneProp) //=== false

      // 此时,如果删除了这个属性,则它会成为一个冻结对象.
      delete oneProp.p
      Object.isFrozen(oneProp) //=== true

      // 一个不可扩展的对象,拥有一个不可写但可配置的属性,则它仍然是非冻结的.
      var nonWritable = { e: 'plep' }
      Object.preventExtensions(nonWritable)
      Object.defineProperty(nonWritable, 'e', { writable: false }) // 变得不可写
      Object.isFrozen(nonWritable) //=== false

      // 把这个属性改为不可配置,会让这个对象成为冻结对象.
      Object.defineProperty(nonWritable, 'e', { configurable: false }) // 变得不可配置
      Object.isFrozen(nonWritable) //=== true

      // 一个不可扩展的对象,拥有一个不可配置但可写的属性,则它仍然是非冻结的.
      var nonConfigurable = { release: 'the kraken!' }
      Object.preventExtensions(nonConfigurable)
      Object.defineProperty(nonConfigurable, 'release', { configurable: false })
      Object.isFrozen(nonConfigurable) //=== false

      // 把这个属性改为不可写,会让这个对象成为冻结对象.
      Object.defineProperty(nonConfigurable, 'release', { writable: false })
      Object.isFrozen(nonConfigurable) //=== true

      // 一个不可扩展的对象,值拥有一个访问器属性,则它仍然是非冻结的.
      var accessor = {
        get food() {
          return 'yum'
        },
      }
      Object.preventExtensions(accessor)
      Object.isFrozen(accessor) //=== false

      // ...但把这个属性改为不可配置,会让这个对象成为冻结对象.
      Object.defineProperty(accessor, 'food', { configurable: false })
      Object.isFrozen(accessor) //=== true

      // 使用Object.freeze是冻结一个对象最方便的方法.
      var frozen = { 1: 81 }
      Object.isFrozen(frozen) //=== false
      Object.freeze(frozen)
      Object.isFrozen(frozen) //=== true

      // 一个冻结对象也是一个密封对象.
      Object.isSealed(frozen) //=== true

      // 当然,更是一个不可扩展的对象.
      Object.isExtensible(frozen) //=== false

      //Object.preventExtensions()方法让一个对象变的不可扩展，也就是永远不能再添加新的属性。
      const object1 = {}

      Object.preventExtensions(object1)

      try {
        Object.defineProperty(object1, 'property1', {
          value: 42,
        })
      } catch (e) {
        console.log(e)
        // expected output: TypeError: Cannot define property property1, object is not extensible
      }

      //Object.seal()方法封闭一个对象，阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
      var obj = {
        prop: function () {},
        foo: 'bar',
      }

      // 可以添加新的属性
      // 可以更改或删除现有的属性
      obj.foo = 'baz'
      obj.lumpy = 'woof'
      delete obj.prop

      var o = Object.seal(obj)

      o === obj // true
      Object.isSealed(obj) // === true

      // 仍然可以修改密封对象的属性值
      obj.foo = 'quux'

      // 但是你不能将属性重新定义成为访问器属性
      // 反之亦然
      Object.defineProperty(obj, 'foo', {
        get: function () {
          return 'g'
        },
      }) // throws a TypeError

      // 除了属性值以外的任何变化，都会失败.
      obj.quaxxor = 'the friendly duck'
      // 添加属性将会失败
      delete obj.foo
      // 删除属性将会失败

      // 在严格模式下，这样的尝试将会抛出错误
      function fail() {
        'use strict'
        delete obj.foo // throws a TypeError
        obj.sparky = 'arf' // throws a TypeError
      }
      fail()

      // 通过Object.defineProperty添加属性将会报错
      Object.defineProperty(obj, 'ohai', {
        value: 17,
      }) // throws a TypeError
      Object.defineProperty(obj, 'foo', {
        value: 'eit',
      }) // 通过Object.defineProperty修改属性值

      //Object.isSealed() 方法判断一个对象是否被密封
      // 新建的对象默认不是密封的.
      var empty = {}
      Object.isSealed(empty) // === false

      // 如果你把一个空对象变的不可扩展,则它同时也会变成个密封对象.
      Object.preventExtensions(empty)
      Object.isSealed(empty) // === true

      // 但如果这个对象不是空对象,则它不会变成密封对象,因为密封对象的所有自身属性必须是不可配置的.
      var hasProp = { fee: 'fie foe fum' }
      Object.preventExtensions(hasProp)
      Object.isSealed(hasProp) // === false

      // 如果把这个属性变的不可配置,则这个属性也就成了密封对象.
      Object.defineProperty(hasProp, 'fee', { configurable: false })
      Object.isSealed(hasProp) // === false
      Object.isSealed(hasProp.fee) // === true

      // 最简单的方法来生成一个密封对象,当然是使用Object.seal.
      var sealed = {}
      Object.seal(sealed)
      Object.isSealed(sealed) // === true

      // 一个密封对象同时也是不可扩展的.
      Object.isExtensible(sealed) // === false

      // 一个密封对象也可以是一个冻结对象,但不是必须的.
      Object.isFrozen(sealed) // === true ，所有的属性都是不可写的
      var s2 = Object.seal({ p: 3 })
      Object.isFrozen(s2) // === false， 属性"p"可写

      var s3 = Object.seal({
        get p() {
          return 0
        },
      })
      Object.isFrozen(s3) // === true ，访问器属性不考虑可写不可写,只考虑是否可配置

      //Object.isExtensible() 方法判断一个对象是否是可扩展的（是否可以在它上面添加新的属性）。
      // 新对象默认是可扩展的.
      var empty = {}
      Object.isExtensible(empty) // === true

      // ...可以变的不可扩展.
      Object.preventExtensions(empty)
      Object.isExtensible(empty) // === false

      // 密封对象是不可扩展的.
      var sealed = Object.seal({})
      Object.isExtensible(sealed) // === false

      // 冻结对象也是不可扩展.
      var frozen = Object.freeze({})
      Object.isExtensible(frozen) // === false

      //Object.is() 方法判断两个值是否为同一个值。
      Object.is('foo', 'foo') // true
      Object.is(window, window) // true

      Object.is('foo', 'bar') // false
      Object.is([], []) // false

      var foo = { a: 1 }
      var bar = { a: 1 }
      Object.is(foo, foo) // true
      Object.is(foo, bar) // false

      Object.is(null, null) // true

      // 特例
      Object.is(0, -0) // false
      Object.is(0, +0) // true
      Object.is(-0, -0) // true
      Object.is(NaN, 0 / 0) // true
    </script>
  </body>
</html>
